package com.edu.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.edu.compile.datasource.DefaultRedisDataSource;
import com.edu.config.EnvConfig;
import com.edu.control.Gateway;
import com.edu.control.model.BootConfig;
import com.edu.control.model.DeployRequest;
import com.edu.model.DeployParams;
import com.edu.model.DeployPrepare;
import com.edu.model.EventDefine;
import com.edu.model.EventVersion;
import com.edu.model.QuotaParams;
import com.edu.model.Response;
import com.edu.model.constant.DeployConstant;
import com.edu.model.constant.EventConstant;
import com.edu.model.constant.FileConfig;
import com.edu.service.DeployService;
import com.edu.service.EventVersionService;
import com.edu.util.LangMap;
import com.edu.util.file.DefaultFileStorage;
import com.edu.util.file.FileUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.edu.model.constant.ResultMsgConstant.ILLEGAL_OPERATE;
import static com.edu.model.constant.ResultMsgConstant.NOT_FOUND_EVENT;
import static com.edu.model.constant.ResultMsgConstant.PARAM_MISSING;
import static com.edu.model.constant.ResultMsgConstant.QUOTA_NOT_NULL;

@Service
public class DeployServiceImpl implements DeployService {

    private static final Logger LOGGER = LoggerFactory.getLogger(DeployServiceImpl.class);

    @Resource
    private EventVersionService eventVersionService;

    @Resource
    private Gateway gateway;

    @Resource
    private EnvConfig envConfig;

    @Override
    public Response deploy(DeployParams deployParams) {

        if (deployParams == null || deployParams.getEventId() == null || deployParams.getOperate() == null) {
            LOGGER.error("param missing: {}", deployParams);
            return Response.fail(PARAM_MISSING);
        }

        EventDefine eventDefine = new EventDefine();

        eventDefine.setEventId(deployParams.getEventId());
        EventVersion eventVersion = eventVersionService.getNewestEventVersion(eventDefine);

        if (eventVersion == null) {
            LOGGER.error("eventId: {}, not found event", deployParams.getEventId());
            return Response.fail(NOT_FOUND_EVENT);
        }

        Integer operate = deployParams.getOperate();
        if (operate == EventConstant.OperateType.COMPILED_STATE || operate == EventConstant.OperateType.STOP_STATE || operate == EventConstant.OperateType.RECOMPILED_AT_RUNNING_STATE) {
            DeployPrepare deployPrepare = DeployPrepare.init(eventVersion, deployParams);
            return doDeploy(deployPrepare);
        } else if (operate == EventConstant.OperateType.RUNNING_STATE) {
            // TODO
            return Response.success();
        } else {
            return Response.fail(ILLEGAL_OPERATE);
        }
    }

    private Response doDeploy(DeployPrepare deployPrepare) {
        Response result;
        result = getQuotaParams(deployPrepare);
        if (result.isFail()) {
            return result;
        }

        result = parseDataSource(deployPrepare.getEventVersion());
        if (result.isFail()) {
            return result;
        }

        result = uploadDataSource(deployPrepare, result.parse());

        if (result.isFail()) {
            return result;
        }

        DeployRequest deployRequest = initDeployRequest(deployPrepare);
        Response deploy = gateway.deploy(deployRequest);
        return deploy;
    }

    private DeployRequest initDeployRequest(DeployPrepare deployPrepare) {
        DeployRequest deployRequest = new DeployRequest();
        deployRequest.setEventId(deployPrepare.getEventVersion().getEventId());
        deployRequest.setFunctionName("event-" + deployPrepare.getEventVersion().getEventId());
        deployRequest.setDeployVersion(deployPrepare.getEventVersion().getVersion());
        deployRequest.setSourceFile(deployPrepare.getEventVersion().getStorePath());
        deployRequest.setDatasource(deployPrepare.getProcessData(DeployConstant.STORE_PATH));
        deployRequest.setDeployType(deployPrepare.getEventVersion().getEventType());
        BootConfig bootConfig = new BootConfig();
        bootConfig.setCpu(deployPrepare.getDeployParams().getQuotaParams().getCpu());
        bootConfig.setMemory(deployPrepare.getDeployParams().getQuotaParams().getMemory());
        bootConfig.setTimeout(deployPrepare.getDeployParams().getQuotaParams().getTimeout());
        bootConfig.setReplicas(deployPrepare.getDeployParams().getQuotaParams().getReplicas());
        bootConfig.setLang(LangMap.getLang(deployPrepare.getEventVersion().getLang()));
        deployRequest.setBootConfig(bootConfig);
        return deployRequest;
    }

    private Response uploadDataSource(DeployPrepare deployPrepare, JSONObject dataSource) {
        final FileUtil fileUtil = new FileUtil();
        String storePath = null;
        Response response = Response.success();
        try {
            String eventId = deployPrepare.getEventVersion().getEventId();
            fileUtil.createFile(FileConfig.DEFAULT_CREATE_PATH + eventId, FileConfig.DATASOURCE_PREFIX, FileConfig.JSON_SUFFIX, EventConstant.DbConstant.DB_FILE_NAME_PREFIX_SIMPLE);
            fileUtil.append(JSONObject.toJSONString(dataSource, SerializerFeature.PrettyFormat));
            fileUtil.toZip();
            storePath = fileUtil.store(new DefaultFileStorage(eventId));
            deployPrepare.putProcessData(DeployConstant.STORE_PATH, storePath);
        } catch (Exception e) {
            LOGGER.error("upload datasource file error {}", e.getMessage());
            response.setSuccess(false);
            response.setMsg("数据源上传失败：" + e.getMessage());
        } finally {
        }
        response.setData(storePath);
        LOGGER.info("datasource storePath {}", storePath);
        return response;
    }

    private Response getQuotaParams(DeployPrepare deployPrepare) {
        QuotaParams quotaParams = deployPrepare.getDeployParams().getQuotaParams();
        if (quotaParams == null) {
            quotaParams = new QuotaParams();
            quotaParams.setCpu(100);
            quotaParams.setMemory(200);
            quotaParams.setTimeout(10);
            quotaParams.setReplicas(1);
            deployPrepare.getDeployParams().setQuotaParams(quotaParams);
        } else {
            if (quotaParams.getCpu() == null || quotaParams.getMemory() == null || quotaParams.getTimeout() == null || quotaParams.getReplicas() == null) {
                return Response.fail(QUOTA_NOT_NULL);
            }
            if (quotaParams.isChanged()) {
                // TODO 保存
            }
        }
        return Response.success();
    }


    protected Response parseDataSource(EventVersion eventVersion) {
        JSONObject dataSourceJson = new JSONObject();
        Response result;

        String dataSource = eventVersion.getDataSource();
        if (StringUtils.isNotBlank(dataSource)) {
            Map<Integer, DefaultRedisDataSource> nodeId_defaultRedisDataSource = new HashMap<>();
            List<JSONObject> dataSourceList = JSONObject.parseArray(dataSource, JSONObject.class);
            for (JSONObject ds : dataSourceList) {
                Integer nodeId = ds.getInteger("ni");
                switch (ds.getInteger("t")) {
                    case EventConstant.DataSourceParseType.DEFAULT_REDIS: {
                        nodeId_defaultRedisDataSource.put(nodeId, JSON.toJavaObject(ds, DefaultRedisDataSource.class));
                        break;
                    }
                }
            }
            result = parseDefaultRedisDataSource(dataSourceJson, nodeId_defaultRedisDataSource);
            if (result.isFail()) {
                return result;
            }
        }
        return Response.success(dataSourceJson);
    }

    private Response parseDefaultRedisDataSource(JSONObject dataSourceJson, Map<Integer, DefaultRedisDataSource> nodeId_defaultRedisDataSource) {
        if (!nodeId_defaultRedisDataSource.isEmpty()) {
            nodeId_defaultRedisDataSource.forEach((nodeId, defaultRedisDataSource) -> {
                defaultRedisDataSource.setNi(null);
                defaultRedisDataSource.setT(null);
                defaultRedisDataSource.setHost(envConfig.getRedisHost());
                defaultRedisDataSource.setIndex(envConfig.getRedisIndex());
                defaultRedisDataSource.setPassword(envConfig.getRedisPassword());
                defaultRedisDataSource.setPort(envConfig.getPort());
                dataSourceJson.put(EventConstant.DbConstant.REDIS_SYMBOL_PREFIX + nodeId, defaultRedisDataSource);
            });
        }
        return Response.success();
    }

}
